﻿using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NLog;
using System.Reflection;
using System.Transactions;

namespace Framework.SqlSugar;

/// <summary>
/// 工作单元配置特性
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public sealed class UnitOfWorkAttribute : Attribute, IAsyncActionFilter, IAsyncPageFilter, IOrderedFilter
{
	private static Logger logger = LogManager.GetCurrentClassLogger();

	/// <summary>
	/// 确保事务可用
	/// <para>此方法为了解决静态类方式操作数据库的问题</para>
	/// </summary>
	public bool EnsureTransaction { get; set; } = false;

	/// <summary>
	/// 是否使用分布式环境事务
	/// </summary>
	public bool UseAmbientTransaction { get; set; } = false;

	/// <summary>
	/// 分布式环境事务范围
	/// </summary>
	/// <remarks><see cref="UseAmbientTransaction"/> 为 true 有效</remarks>
	public TransactionScopeOption TransactionScope { get; set; } = TransactionScopeOption.Required;

	/// <summary>
	/// 分布式环境事务隔离级别
	/// </summary>
	/// <remarks><see cref="UseAmbientTransaction"/> 为 true 有效</remarks>
	public IsolationLevel TransactionIsolationLevel { get; set; } = IsolationLevel.ReadCommitted;

	/// <summary>
	/// 分布式环境事务超时时间
	/// </summary>
	/// <remarks>单位秒</remarks>
	public int TransactionTimeout { get; set; } = 0;

	/// <summary>
	/// 支持分布式环境事务异步流
	/// </summary>
	/// <remarks><see cref="UseAmbientTransaction"/> 为 true 有效</remarks>
	public TransactionScopeAsyncFlowOption TransactionScopeAsyncFlow { get; set; } = TransactionScopeAsyncFlowOption.Enabled;

	/// <summary>
	///  MiniProfiler 分类名
	/// </summary>
	private const string MiniProfilerCategory = "unitOfWork";

	/// <summary>
	/// 过滤器排序
	/// </summary>
	private const int FilterOrder = 9999;

	/// <summary>
	/// 排序属性
	/// </summary>
	public int Order => FilterOrder;

	/// <summary>
	/// 构造函数
	/// </summary>
	public UnitOfWorkAttribute()
	{
	}

	/// <summary>
	/// 构造函数
	/// </summary>
	/// <param name="ensureTransaction"></param>
	public UnitOfWorkAttribute(bool ensureTransaction)
	{
		EnsureTransaction = ensureTransaction;
	}

	/// <summary>
	/// 拦截请求
	/// </summary>
	/// <param name="context">动作方法上下文</param>
	/// <param name="next">中间件委托</param>
	/// <returns></returns>
	public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
	{
		// 获取动作方法描述器
		var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
		var method = actionDescriptor.MethodInfo;

		// 创建分布式环境事务
		var transactionScope = CreateTransactionScope(context);

		try
		{
			//// 打印工作单元开始消息
			//if (UseAmbientTransaction) App.PrintToMiniProfiler(MiniProfilerCategory, "Beginning (Ambient)");

			logger.Warn("[Database Transaction] Starting a new transaction.");

			// 开始事务
			BeginTransaction(context, method, out var _unitOfWork, out var unitOfWorkAttribute);

			// 获取执行 Action 结果
			var resultContext = await next();

			// 提交事务
			CommitTransaction(context, _unitOfWork, unitOfWorkAttribute, resultContext);

			// 提交分布式环境事务
			if (resultContext.Exception == null)
			{
				transactionScope?.Complete();

				logger.Warn("[Database Transaction] Transaction committed successfully.");

				//// 打印事务提交消息
				//if (UseAmbientTransaction) App.PrintToMiniProfiler(MiniProfilerCategory, "Completed (Ambient)");
			}
			else
			{
				//// 打印事务回滚消息
				//if (UseAmbientTransaction) App.PrintToMiniProfiler(MiniProfilerCategory, "Rollback (Ambient)", isError: true);

				logger.Error(resultContext.Exception, "[Database Transaction] Transaction rolled back due to an error.");
			}
		}
		catch (Exception ex)
		{
			logger.Error(ex, "[Database Transaction] Transaction rolled back due to an error.");

			//// 打印事务回滚消息
			//if (UseAmbientTransaction) App.PrintToMiniProfiler(MiniProfilerCategory, "Rollback (Ambient)", isError: true);

			throw;
		}
		finally
		{
			transactionScope?.Dispose();
		}
	}

	/// <summary>
	/// 模型绑定拦截
	/// </summary>
	/// <param name="context"></param>
	/// <returns></returns>
	public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
	{
		return Task.CompletedTask;
	}

	/// <summary>
	/// 拦截请求
	/// </summary>
	/// <param name="context"></param>
	/// <param name="next"></param>
	/// <returns></returns>
	public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
	{
		// 获取动作方法描述器
		var method = context.HandlerMethod?.MethodInfo;
		// 处理 Blazor Server
		if (method == null)
		{
			_ = await next.Invoke();
			return;
		}

		// 创建分布式环境事务
		var transactionScope = CreateTransactionScope(context);

		try
		{
			//// 打印工作单元开始消息
			//if (UseAmbientTransaction) App.PrintToMiniProfiler(MiniProfilerCategory, "Beginning (Ambient)");

			logger.Warn("[Database Transaction] Starting a new transaction.");

			// 开始事务
			BeginTransaction(context, method, out var _unitOfWork, out var unitOfWorkAttribute);

			// 获取执行 Action 结果
			var resultContext = await next.Invoke();

			// 提交事务
			CommitTransaction(context, _unitOfWork, unitOfWorkAttribute, resultContext);

			// 提交分布式环境事务
			if (resultContext.Exception == null)
			{
				transactionScope?.Complete();

				logger.Warn("[Database Transaction] Transaction committed successfully.");

				//// 打印事务提交消息
				//if (UseAmbientTransaction) App.PrintToMiniProfiler(MiniProfilerCategory, "Completed (Ambient)");
			}
			else
			{
				//// 打印事务回滚消息
				//if (UseAmbientTransaction) App.PrintToMiniProfiler(MiniProfilerCategory, "Rollback (Ambient)", isError: true);

				logger.Error(resultContext.Exception, "[Database Transaction] Transaction rolled back due to an error.");
			}
		}
		catch (Exception ex)
		{
			logger.Error(ex, "[Database Transaction] Transaction rolled back due to an error.");

			//// 打印事务回滚消息
			//if (UseAmbientTransaction) App.PrintToMiniProfiler(MiniProfilerCategory, "Rollback (Ambient)", isError: true);

			throw;
		}
		finally
		{
			transactionScope?.Dispose();
		}
	}

	/// <summary>
	/// 创建分布式环境事务
	/// </summary>
	/// <param name="context"></param>
	/// <returns></returns>
	private TransactionScope CreateTransactionScope(FilterContext context)
	{
		// 是否启用分布式环境事务
		var transactionScope = UseAmbientTransaction
			 ? new TransactionScope(TransactionScope,
			new TransactionOptions { IsolationLevel = TransactionIsolationLevel, Timeout = TransactionTimeout > 0 ? TimeSpan.FromSeconds(TransactionTimeout) : default }
			, TransactionScopeAsyncFlow)
			 : default;

		
		return transactionScope;
	}

	/// <summary>
	/// 开始事务
	/// </summary>
	/// <param name="context"></param>
	/// <param name="method"></param>
	/// <param name="_unitOfWork"></param>
	/// <param name="unitOfWorkAttribute"></param>
	private static void BeginTransaction(FilterContext context, MethodInfo method, out IUnitOfWork _unitOfWork, out UnitOfWorkAttribute unitOfWorkAttribute)
	{
		// 解析工作单元服务
		_unitOfWork = context.HttpContext.RequestServices.GetRequiredService<IUnitOfWork>();

		// 获取工作单元特性
		unitOfWorkAttribute = method.GetCustomAttribute<UnitOfWorkAttribute>();

		// 调用开启事务方法
		_unitOfWork.BeginTransaction(context, unitOfWorkAttribute);

		//// 打印工作单元开始消息
		//if (!unitOfWorkAttribute.UseAmbientTransaction) App.PrintToMiniProfiler(MiniProfilerCategory, "Beginning");
	}

	/// <summary>
	/// 提交事务
	/// </summary>
	/// <param name="context"></param>
	/// <param name="_unitOfWork"></param>
	/// <param name="unitOfWorkAttribute"></param>
	/// <param name="resultContext"></param>
	private static void CommitTransaction(FilterContext context, IUnitOfWork _unitOfWork, UnitOfWorkAttribute unitOfWorkAttribute, FilterContext resultContext)
	{
		// 获取动态结果上下文
		dynamic dynamicResultContext = resultContext;

		if (dynamicResultContext.Exception == null)
		{
			// 调用提交事务方法
			_unitOfWork.CommitTransaction(resultContext, unitOfWorkAttribute);
		}
		else
		{
			// 调用回滚事务方法
			_unitOfWork.RollbackTransaction(resultContext, unitOfWorkAttribute);
		}

		// 调用执行完毕方法
		_unitOfWork.OnCompleted(context, resultContext);

		//// 打印工作单元结束消息
		//if (!unitOfWorkAttribute.UseAmbientTransaction) App.PrintToMiniProfiler(MiniProfilerCategory, "Ending");
	}
}
